/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <QDebug>
#include <QMenu>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QLinearGradient>
#include <QLineEdit>
#include <QSpinBox>
#include <QRect>
#include <QMouseEvent>

#include "procycle.h"
#include "tasktreeview.h"
#include "tasktreemodel.h"
#include "tasktreecolumnselectdialog.h"
#include "taskviewhelper.h"
#include "taskselectionmodel.h"


QSize TaskTreeItemDelegate::sizeHint(
				const QStyleOptionViewItem &option,
				const QModelIndex &index) const
{
		QSize s = QStyledItemDelegate::sizeHint(option, index);
		s.setHeight(m_app->rowHeight());
		return s;
}


void TaskTreeItemDelegate::paint(
				QPainter *painter,
				const QStyleOptionViewItem &option,
				const QModelIndex &index) const
{
		bool drawNum = (index.column() == 1);
		bool drawLeft = (index.column() != 0);

		if (drawNum) {
				int num = index.data().toInt();

				painter->save();
				QLinearGradient grad(0, option.rect.top(), 0, option.rect.bottom());
				grad.setColorAt(0, QColor(0xe0, 0xe0, 0xe0));
				grad.setColorAt(1, QColor(0xc0, 0xc0, 0xc0));
				painter->fillRect(option.rect, grad);
                if (num != -1) { // -1 is for the appending line
                    QPen pen (Qt::black);
                    pen.setWidth(1);
                    painter->setPen(pen);
                    painter->drawText(option.rect, QString::number(num), QTextOption(Qt::AlignCenter));
                }
				painter->restore();
		}
		else {
				QStyledItemDelegate::paint(painter, option, index);
		}

		painter->save();
		QPen pen (Qt::gray);
		pen.setWidth(1);
		painter->setPen(pen);
		if (drawLeft) {
				painter->drawRect(option.rect);
		}
		else {
				QRect r (option.rect.topLeft(), QSize(option.rect.width()+1, option.rect.height()+1));
				painter->drawLine(r.topLeft(), r.topRight());
				painter->drawLine(r.topRight(), r.bottomRight());
				painter->drawLine(r.bottomRight(), r.bottomLeft());
		}
		painter->restore();

}


QWidget* TaskTreeItemDelegate::createEditor(
				QWidget *parent,
				const QStyleOptionViewItem &option,
				const QModelIndex &index) const
{
		PC::Task::Data td = app()->taskTreeModel()->column(index.column());
		switch (td) {
		case PC::Task::DATA_NAME:
				return new QLineEdit(parent);
				break;
		case PC::Task::DATA_LENGTH: {
						QSpinBox* editor = new QSpinBox(parent);
						editor->setMinimum(0);
						return editor;
				}
				break;
		}
		return QStyledItemDelegate::createEditor(parent, option, index);
}


void TaskTreeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {
		QVariant value = index.data(Qt::EditRole);
		PC::Task::Data td = app()->taskTreeModel()->column(index.column());
		switch (td) {
		case PC::Task::DATA_NAME: {
						QLineEdit* ed = static_cast<QLineEdit*>(editor);
						ed->setText(value.toString());
				}
				break;
		case PC::Task::DATA_LENGTH: {
						QSpinBox* ed = static_cast<QSpinBox*>(editor);
						ed->setValue(value.toInt());
				}
				break;
		}
}

void TaskTreeItemDelegate::setModelData(
				QWidget *editor,
				QAbstractItemModel *model,
				const QModelIndex &index) const
{
		PC::Task::Data td = app()->taskTreeModel()->column(index.column());
		switch (td) {
		case PC::Task::DATA_NAME: {
						QLineEdit* ed = static_cast<QLineEdit*>(editor);
						model->setData(index, QVariant(ed->text()));
				}
				break;
		case PC::Task::DATA_LENGTH: {
						QSpinBox* ed = static_cast<QSpinBox*>(editor);
						model->setData(index, QVariant(ed->value()));
				}
				break;
		}
}


void TaskTreeItemDelegate::updateEditorGeometry(
				QWidget *editor,
				const QStyleOptionViewItem &option,
				const QModelIndex&) const
{
		editor->setGeometry(option.rect);
}










TaskTreeHeader::TaskTreeHeader(ProCycle *app, QWidget *parent) :
QHeaderView (Qt::Horizontal, parent),
		PCChild(app)
{
		m_colNames[PC::Task::DATA_NUM] = tr("");
		m_colNames[PC::Task::DATA_TBSID] = tr("TBS ID");
		m_colNames[PC::Task::DATA_POS] = tr("Pos");
		m_colNames[PC::Task::DATA_NAME] = tr("Name");
		m_colNames[PC::Task::DATA_LENGTH] = tr("Length");
		m_colNames[PC::Task::DATA_START] = tr("Start");
		m_colNames[PC::Task::DATA_FINISH] = tr("Finish");
		m_colNames[PC::Task::DATA_LATESTART] = tr("Late Start");
		m_colNames[PC::Task::DATA_LATEFINISH] = tr("Late Finish");
		m_colNames[PC::Task::DATA_PREDECESSORS] = tr("Predecessors");

		m_colSizes[PC::Task::DATA_NUM] = 30;
		m_colSizes[PC::Task::DATA_TBSID] = 60;
		m_colSizes[PC::Task::DATA_POS] = 30;
		m_colSizes[PC::Task::DATA_NAME] = 200;
		m_colSizes[PC::Task::DATA_LENGTH] = 70;
		m_colSizes[PC::Task::DATA_START] = 70;
		m_colSizes[PC::Task::DATA_FINISH] = 70;
		m_colSizes[PC::Task::DATA_LATESTART] = 70;
		m_colSizes[PC::Task::DATA_LATEFINISH] = 70;
        m_colSizes[PC::Task::DATA_PREDECESSORS] = 70;

		setContextMenuPolicy(Qt::CustomContextMenu);

		QObject::connect(this, SIGNAL(customContextMenuRequested(QPoint)),
										 this, SLOT(contextMenu(QPoint)));

		QObject::connect(this, SIGNAL(sectionResized(int,int,int)),this,
										 SLOT(userColumnResize(int, int, int)));
}


QSize TaskTreeHeader::sizeHint() const {
    QSize s = QHeaderView::sizeHint();
    s.setHeight(m_app->headerHeight());
    return s;
}

void TaskTreeHeader::contextMenu(QPoint point) {
    QMenu menu (m_app);
    menu.addAction(QIcon(), tr("organize columns ..."), this, SLOT(userColumnSelect()));
    menu.exec(mapToGlobal(point));
}

void TaskTreeHeader::userColumnSelect() {
    TaskTreeColumnSelectDialog dlg (m_app);
    dlg.setModal(true);
    dlg.show();
    if (dlg.exec() == QDialog::Accepted) {
        m_app->taskTreeModel()->setColumns(dlg.columns());
    }
}


void TaskTreeHeader::resizeColumns() {
		int count = model()->columnCount();
		for (int i=0; i<count; i++) {
				PC::Task::Data td = m_app->taskTreeModel()->column(i);
				resizeSection(i, m_colSizes[td]);
		}
}

void TaskTreeHeader::userColumnResize(int section, int /*oldSize*/, int newSize) {
		PC::Task::Data td = m_app->taskTreeModel()->column(section);
		m_colSizes[td] = newSize;
		emit userColumnResized(td, newSize);
}


void TaskTreeHeader::paintSection ( QPainter* painter, const QRect & rect, int logicalIndex ) const {
		painter->save();

		QLinearGradient grad(0, rect.top(), 0, rect.bottom());
		grad.setColorAt(0, QColor(0xe0, 0xe0, 0xe0));
		grad.setColorAt(1, QColor(0xc0, 0xc0, 0xc0));
		painter->fillRect(rect, grad);

		QPen textPen (Qt::black);
		painter->setPen(textPen);
		QString text (m_app->taskTreeModel()->headerData(logicalIndex, Qt::Horizontal, Qt::DisplayRole).toString());
		QRect r (rect.left()+10, rect.top(), rect.width()-10, rect.height());
		painter->drawText(r, text, QTextOption(Qt::AlignLeft | Qt::AlignVCenter));

		QPen borderPen (Qt::gray);
		borderPen.setWidth(1);
		painter->setPen(borderPen);
		painter->drawRect(rect);

		painter->restore();
}







TaskTreeView::TaskTreeView(ProCycle* app, QWidget *parent) :
	QTreeView(parent),
	PCChild (app)
{
	setUniformRowHeights(true);
	setItemDelegate(new TaskTreeItemDelegate(app));
	setHeader(new TaskTreeHeader(app, this));

	connect(this, SIGNAL(expanded(QModelIndex)), SLOT(nodeExpanded(QModelIndex)));
	connect(this, SIGNAL(collapsed(QModelIndex)), SLOT(nodeCollapsed(QModelIndex)));
}



void TaskTreeView::initSizePolicy() {
    int sum=0, count = header()->count();
    for(int i=0; i<count; ++i) {
        sum += header()->sectionSize(i);
    }
    float ratio = width()/static_cast<float>(sum);
    for(int i=0; i<count; ++i) {
        header()->resizeSection(i, header()->sectionSize(i) * ratio);
    }
}

TaskTreeModel* TaskTreeView::taskModel() {
	return static_cast<TaskTreeModel*>(model());
}


void TaskTreeView::expandAllTasks() {
	TaskViewHelper tvh (app()->activeCycle());
	tvh.setExpanded(true, true);
	expandAll();
}


void TaskTreeView::syncExpansion(const QModelIndex &index, bool recursive) {
	if (index.isValid()) {
		bool b = this->blockSignals(true);
		TaskViewHelper tvh (taskModel()->taskForIndex(index));
		if (tvh.isExpanded()) {
			expand(index);
		}
		else {
			collapse(index);
		}
		this->blockSignals(b);
	}
	if (recursive) {
		int count = model()->rowCount(index);
		for(int i=0; i<count; i++) {
			syncExpansion(model()->index(i, 0, index), recursive);
		}
	}
}

bool isSelectionSource = false;

void TaskTreeView::selectTask(PC::Task *selected, PC::Task* unselected) {
	if (!isSelectionSource) {
		isSelectionSource = true;
		QModelIndex i;
		if (unselected) {
			i = taskModel()->indexForTask(unselected);
			selectionModel()->select(i, QItemSelectionModel::Deselect
									 | QItemSelectionModel::Rows);
		}
		if (selected) {
			i = taskModel()->indexForTask(selected);
			selectionModel()->select(i, QItemSelectionModel::Select
									 | QItemSelectionModel::Rows);
		}
		isSelectionSource = false;
	}
}


void TaskTreeView::selectionChanged(const QItemSelection &selected,
									const QItemSelection &deselected) {
	QTreeView::selectionChanged(selected, deselected);
	if (!isSelectionSource) {
		isSelectionSource = true;
		QModelIndexList l = selected.indexes();
		PC::Task* selectedTask = 0;
		if (l.count() > 0) {
			QModelIndex i = l.at(0);
			selectedTask = taskModel()->taskForIndex(i);
		}
		app()->selectionModel()->selectTask(selectedTask);
		isSelectionSource = false;
	}
}


void TaskTreeView::drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const {

	if (model()->hasChildren(index)) {
		const int nodeside = 8;

		painter->save();

		QRect r (rect);
		r.setLeft(rect.right() - indentation());
		r.setSize(QSize(nodeside, nodeside));
		QPoint tl (r.left()+(indentation()-nodeside)/2+1, r.top()+(app()->rowHeight()-nodeside)/2);
		r.moveTopLeft(tl);

		QLinearGradient grad (0, r.top(), 0, r.bottom());
		grad.setColorAt(0, QColor(Qt::white));
		grad.setColorAt(1, QColor(0xe0, 0xe0, 0xe0));
		painter->fillRect(r, grad);

		QPen inside (Qt::black);
		inside.setWidth(1);
		painter->setPen(inside);
		painter->drawLine(r.left()+2, r.top()+nodeside/2, r.right()-1, r.top()+nodeside/2);
		if (!isExpanded(index)) {
			painter->drawLine(r.left()+nodeside/2, r.top()+2, r.left()+nodeside/2, r.bottom()-1);
		}

		QPen outside (Qt::gray);
		outside.setWidth(1);
		painter->setPen(outside);
		painter->drawRect(r);

		painter->restore();
	}

	painter->save();
	QPen pen (Qt::gray);
	pen.setWidth(1);
	painter->setPen(pen);
	QRect r (rect.topLeft(), QSize(rect.width()+1, rect.height()+1));
	painter->drawLine(r.bottomRight(), r.bottomLeft());
	painter->drawLine(r.bottomLeft(), r.topLeft());
	painter->drawLine(r.topLeft(), r.topRight());
	painter->restore();
}


void TaskTreeView::nodeExpanded(const QModelIndex &index) {
	PC::Task* t = taskModel()->taskForIndex(index);
	TaskViewHelper tvh(t);
	tvh.setExpanded(true);
	emit taskExpansionChanged(t);
}

void TaskTreeView::nodeCollapsed(const QModelIndex &index) {
	PC::Task* t = taskModel()->taskForIndex(index);
	TaskViewHelper tvh(t);
	tvh.setExpanded(false);
	emit taskExpansionChanged(t);
}
